// Copyright (c) 2007 Robin Boerdijk. All rights reserved.
//
// This software is licensed under the terms of the Microsoft Reciprocal License as described 
// in the file LICENSE.txt, which you should have received as part of this distribution. The 
// license terms are also available at http://www.opensource.org/licenses/ms-rl.html.

#include "wtkpc.h"

#include "wtk/Window.h"
#include "wtk/MenuCommand.h"
#include "wtk/ClientDC.h"
#include "wtk/Error.h"

#include <commctrl.h>
#include <cassert>

#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#include <iostream>

namespace wtk
{
	Window::Window(const wchar_t *className, Window *parentWindow)
		: className(className)
		, parentWindow(parentWindow)
		, style(0)
		, styleEx(0)
		, position(Zero<POINT>())
		, size(Zero<SIZE>())
		, font(NULL)
		, handle(NULL)
	{
	}

	Window::~Window()
	{
	}

	void Window::Create(const wchar_t *title, HMENU menuHandle)
	{
		HWND parentWindowHandle = (this->parentWindow == NULL) ? NULL : this->parentWindow->handle;
		this->handle = win32::CreateWindowExW(this->styleEx, this->className, title, this->style, this->position.x, this->position.y, 
			this->size.cx, this->size.cy, parentWindowHandle, menuHandle, NULL, NULL);
		win32::SendMessageW<void>(this->handle, WM_SETFONT, GetFont(), FALSE);
		win32::SetWindowSubclass(this->handle, SubclassProc, UINT_PTR(this), NULL);
	}

	void Window::Destroy()
	{
		win32::RemoveWindowSubclass(this->handle, SubclassProc, UINT_PTR(this));
		win32::DestroyWindow(this->handle);
	}

	void Window::SetWindowPos(HWND insertAfterWindowHandle, UINT flags)
	{
		win32::SetWindowPos(this->handle, insertAfterWindowHandle, this->position.x, this->position.y, 
			this->size.cx, this->size.cy, flags);
	}

	HDWP Window::DeferWindowPos(HDWP dwpHandle, HWND insertAfterWindowHandle, UINT flags)
	{
		return win32::DeferWindowPos(dwpHandle, this->handle, insertAfterWindowHandle, this->position.x, this->position.y, 
			this->size.cx, this->size.cy, flags);
	}

	std::wstring Window::GetText()
	{
		const int MaxCount = 32 * 1024;

		int length = GetTextLength();
		if (length < MaxCount) {
			wchar_t buffer[MaxCount];
			length = GetText(buffer, length);
			return std::wstring(buffer, length);
		} else {
			std::vector<wchar_t> buffer(length + 1);
			length = GetText(&buffer[0], length);
			return std::wstring(&buffer[0], length);
		}
	}

	LRESULT Window::ProcessMessage(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch (message)
		{
		case WM_MENUCOMMAND:
			return ProcessMenuCommandMessage(HMENU(lParam), UINT(wParam));
		default:
			return DefSubclassProc(windowHandle, message, wParam, lParam);
		}
	}

	// static
	LRESULT Window::SubclassProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR)
	{
		try {
			return reinterpret_cast<Window *>(id)->ProcessMessage(windowHandle, message, wParam, lParam);
		} catch (const std::exception &error) {
			win32::MessageBoxA(NULL, error.what(), "Error", MB_ICONERROR | MB_OK);
			return 0;
		}
	}

	LRESULT Window::ProcessMenuCommandMessage(HMENU menuHandle, UINT selectedItemIndex)
	{
		MENUITEMINFOW menuItemInfo;
		menuItemInfo.cbSize = sizeof(menuItemInfo);
		menuItemInfo.fMask = MIIM_DATA;

		win32::GetMenuItemInfoW(menuHandle, selectedItemIndex, TRUE, &menuItemInfo);

		if (menuItemInfo.dwItemData != NULL) {
			MenuCommand *menuCommand = reinterpret_cast<MenuCommand *>(menuItemInfo.dwItemData);
			menuCommand->m_selectEvent.Signal(menuCommand);
		}

		return 0;
	}

	HFONT Window::GetFont()
	{
		if (this->font != NULL) {
			return this->font;
		} else if (this->parentWindow != NULL) {
			return this->parentWindow->GetFont();
		} else {
			return win32::GetStockObject<HFONT>(DEFAULT_GUI_FONT);
		}
	}

	SIZE Window::GetTextBoxSize(const std::wstring &text)
	{
		ClientDC dc(win32::GetDesktopWindow());
		win32::SelectObject(dc, GetFont()); 
		return dc.GetTextBoxSize(text.c_str());
	}
}
